home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
asmbler.arc
/
PAINT.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-11-19
|
20KB
|
589 lines
page 60,132
title PAINT
public paint
; Routine to fill an area
; Ref: Christopher Morgan, Bluebook of Assembly Routines for the
; IBM PC & XT, Plume/Waite (1984), pp. 156-160
;
; Callable from Pascal as
;
; PROCEDURE PAINT (x,y,color : integer);
;
; This routine fills an area on the graphics screen with a specified
; color. It starts "painting" at a specified interior position filling
; a region bounded by a "boundary" color.
;
; The region must be completely surrounded by a boundary drawn in the
; boundary color. Any paint color in the region can obstruct the
; filling process, acting just like a boundary. This algorithm uses its
; own stack. If the region is too complex, this stack will overflow.
; There is no check for such a stack overflow, but a check can easily be
; added.
;
; Upon entry:
;
; x-coordinate of interior point is in SI
; y-coordinate of interior point is in DI
; paint color is in low byte of global variable "color"
; boundary color is in high byte of global variable "color"
; ES points to video RAM (IBM color display at B800h)
; DS points to data segment of SETPT and LOCATE routines
;
; Coordinate system origin is at top left corner, with y
; increasing downward.
;
; Output:
; just to screen
;
; Registers modified:
; none
;
; Preliminary push and pop procedures
;
; pushpaint pushes x- and y- coordinates on paint stack
;=======================================================================
; Quadscreen monochrome video buffer stores bits in each scanline from
; 0..1023 in 128 consecutive bytes,although only 0..967 are visible.
; Successive scan lines are in consecutive groups of 128 bytes, with
; scan lines 0..511 filling exactly 64K bytes. A second 64K byte image
; immediately follows, but for the moment, we ignore it and assume that
; it is not selected.
quadscreen SEGMENT AT 0C000h
dw 20000 dup (?)
quadscreen ENDS
;=======================================================================
stacksize equ 2048d
paintstack SEGMENT
dw stacksize dup (?)
paintstack ENDS
;=======================================================================
$paint SEGMENT PARA PUBLIC 'CODE'
scr_top equ 0d ; top screen line
scr_bot equ 511d ; bottom screen line
scr_right equ 1023d ; right screen column (only 0..967 displayable)
scr_left equ 0d ; left screen column
y_inc equ (scr_right+1)/8 ; bytes/scanline
; local constants
bmask db 80h, 40h, 20h, 10h, 08h, 04h, 02h, 01h
; masks to select right bits in word indexed by offset
ltable dw 0FFFFh, 07FFFh, 03FFFh, 01FFFh
dw 00FFFh, 007FFh, 003FFh, 001FFh
dw 000FFh, 0007Fh, 0003Fh, 0001Fh
dw 0000Fh, 00007h, 00003h, 00001h
; masks to select left bits in word indexed by offset
rtable dw 08000h, 0C000h, 0E000h, 0F000h
dw 0F800h, 0FC00h, 0FE00h, 0FF00h
dw 0FF80h, 0FFC0h, 0FFE0h, 0FFF0h
dw 0FFF8h, 0FFFCh, 0FFFEh, 0FFFFh
; local variables
color dw ?
x1 dw ?
x2 dw ?
y1 dw ?
y2 dw ?
yoff dw ?
ycount dw ?
x1woff dw ?
x2woff dw ?
;-----------------------------------------------------------------------
assume cs:$paint,ds:$paint
assume es:quadscreen
locate PROC NEAR
;
; Return pixel color at point (X,Y).
;
; Input:
; X in SI
; Y in DI
; ES points to screen RAM
;
; Output:
; Pixel color (0-1) in AL
;
; Registers modified:
; Only AX
;
push bx ; save registers
push si
mov ax,si ; x
shr si,1 ; x/2
shr si,1 ; x/4
shr si,1 ; (x/8) = byte number
and ax,07h ; bit offset in byte
mov bx,di ; y
shl bx,1 ; y*2
shl bx,1 ; y*4
shl bx,1 ; y*8
shl bx,1 ; y*16
shl bx,1 ; y*32
shl bx,1 ; y*64
shl bx,1 ; y*128
mov ah,es:[bx+si] ; Get byte from y*128 + (x/8)
xor bh,bh ; clear top of bx
mov bh,al ; bit offset into bx
xor al,al ; al=0
and ah,bmask[bx] ; mask all but selected bit
jz locate_done ; return with al=0
mov al,1 ; return with al=1
locate_done:
pop si ; restore registers
pop bx
ret ; return to caller
locate ENDP
;-----------------------------------------------------------------------
assume cs:$paint,ds:$paint
assume es:quadscreen
fill_partial_box PROC NEAR
;
; Common routine to fill partial word at same offset in scanline group
;
; Input:
; ax - mask to select bits in word
; bx - word offset of word containing starting x in scanline
; di - fill color
; es - video RAM segment
; ycount - scan line count
; yoff - starting scan line byte offset in video RAM
;
; Output:
; Just to screen
;
; Registers modified:
; ax,bx,cx,dx,bp,si
;
mov bp,di ; fill color
and bp,ax ; select bits x1..x2
not ax ; mask to exclude bits x1..x2
shl bx,1 ; convert word offset to byte offset
mov si,yoff ; scanline offset in video RAM
add si,bx ; si <- yoff+bx for address of first word
mov cx,ycount ; outer loop count of scan lines
mov bx,y_inc ; scanline increment
fill_partial_1:
mov dx,es:[si] ; word from video RAM
and dx,ax ; clear selected bits
or dx,bp ; set selected bits
mov es:[si],dx ; store word back in video RAM
add si,bx ; point to next scan line
loop fill_partial_1 ; decrement cx, loop while cx > 0
ret ; return to caller
fill_partial_box ENDP
;-----------------------------------------------------------------------
assume cs:$paint,ds:$paint
assume es:quadscreen
setbox PROC NEAR
;
; Fill box with upper left corner at (x1,y1) and lower right corner at
; (x2,y2) with value 0..1 in color.
;
; Input:
; Global variables x1,y1,x2,y2,color
; ES points to screen RAM
;
; Output:
; Just to screen
;
; Registers modified:
; None
;
; Note:
; Assumes (and exits immediately if not so)
; 0 <= x1 <= x2 <= 1023
; 0 <= y1 <= y2 <= 511
;
push ax ; save registers
push bx
push cx
push dx
push bp
push di
push si
mov di,0FFFFh ; fill with ones
test color,di ; test color
jz setbox_1 ; yes, ones
xor di,di ; fill with zeros
setbox_1:
mov ax,x1
cmp ax,x2 ; x2 >= x1?
jna setbox_2 ; yes
jmp setbox_done ; no, exit immediately
setbox_2:
mov cx,y2
inc cx ; y2 + 1
sub cx,y1 ; scan line count = y2 + 1 - y1
test cx,cx ; count > 0?
jg setbox_3 ; yes
jmp setbox_done ; no, exit immediately
setbox_3:
mov ycount,cx ; save scan line count
mov si,y1 ; y1
shl si,1 ; y1*2
shl si,1 ; y1*4
shl si,1 ; y1*8
shl si,1 ; y1*16
shl si,1 ; y1*32
shl si,1 ; y1*64
shl si,1 ; y1*128
mov yoff,si ; yoff = y1*128
; The rectangle fill divides into two cases, (a) x1..x2 in one word, and
; (b) x1..x2 spanning more than one word. In order to avoid unnecessary
; recomputation, the fill in case (b) is split into three loops, first
; the left partial word, then the run of complete words, and last, the
; right partial word. This gives three tight loops with an absolute
; minimum of computation and memory references restricted to the single
; fetch and store of the video RAM data, with all other quantities in
; registers. If the left and/or right partial words are in fact
; complete words, their corresponding loops are skipped and the count
; and start of the complete word loop are correspondingly adjusted so as
; to include them.
mov bx,x1
and bx,0FFF0h ; word offset for x1
mov x1woff,bx ; save word offset
mov bp,x2
and bp,0FFF0h ; word offset for x2
mov x2woff,bp ; save word offset
cmp bp,bx ; x2 in same word as x1?
ja setbox_4 ; no
mov bx,x1 ; yes, special case -- x1..x2 in one word
and bx,0Fh ; bit offset in word
mov ax,rtable[bx] ; mask to choose bits at right
mov bp,x2
and bp,0Fh ; bit offset in word
and ax,ltable[bp] ; combine masks to select bits x1..x2
mov bx,x1woff
call fill_partial_box
jmp setbox_done ; exit
;
; General case -- x1..x2 span one or more words, split into three loops
;
;
; First loop - set bits in partial word left of scanline
;
setbox_4:
mov bp,x1
and bp,0Fh ; bit offset in word
jz setbox_5 ; skip first loop if fullword
mov ax,rtable[bp] ; select mask for bits to right
mov bx,x1woff ; word offset
call fill_partial_box
;
; Second loop - set complete words in scanlines
;
setbox_5:
mov bx,x1woff ; word offset
mov bp,x2woff ; word offset
dec bp
sub bp,bx ; tentative count of complete words to fill
test x1,0Fh ; first word complete?
jnz setbox_6 ; no
inc bp ; yes, include it in loop 2
jmp short setbox_7
setbox_6:
inc bx ; partial word - adjust offset to 1st fullword
setbox_7:
mov ax,x2
and ax,0Fh ; bit offset in last word
cmp ax,0Fh ; last word complete?
jne setbox_8 ; no
inc bp ; yes, include it in loop 2
setbox_8:
test bp,bp ; count >= 0?
jna setbox_10 ; skip loop if 2 adjacent partial words
mov ax,di ; fill color
shl bx,1 ; convert word offset to byte offset
mov si,yoff ; scanline offset in video RAM
add si,bx ; si <- yoff+bx for address of first word
mov cx,ycount ; outer loop count of scan lines
mov bx,y_inc ; scanline increment
cld ; clear direction flag for increment of di
; Loop 2 -- run of full words
setbox_9:
mov di,si ; address of first full words
mov dx,cx ; save outer loop count
mov cx,bp ; inner loop count
rep stosw ; loop filling ax into [es:di]
mov cx,dx ; restore output loop count
add si,bx ; point to next scan line
loop setbox_9 ; decr cx, loop while cx > 0
mov di,ax ; restore fill color
;
; Set bits in partial word right of scanline
;
setbox_10:
mov bp,x2
and bp,0Fh ; bit offset
cmp bp,0Fh ; fullword?
je setbox_done ; yes, already filled by loop 2
mov ax,ltable[bp] ; mask to select bits 0..x2
mov bx,x2woff ; word offset
call fill_partial_box
setbox_done: ; restore registers
pop si
pop di
pop bp
pop dx
pop cx
pop bx
pop ax
ret ; return to caller
setbox ENDP
;-----------------------------------------------------------------------
assume cs:$paint,ds:$paint
pushpaint PROC NEAR
dec bp ; bp is paint stack pointer
dec bp ; and gets decremented first
mov [bp],si ; push x
dec bp
dec bp
mov [bp],di ; push y
ret
pushpaint ENDP
;-----------------------------------------------------------------------
assume cs:$paint,ds:$paint
poppaint PROC NEAR
;
; poppaint pops x- and y-coordinates off paint stack
;
mov di,[bp] ; pop y
inc bp ; increment paint stack pointer
inc bp
mov di,[bp] ; pop x
inc bp ; increment paint stack pointer
inc bp
ret
poppaint ENDP
;-----------------------------------------------------------------------
argcnt equ 3
x equ [bp]
y equ [bp-2]
colval equ [bp-4]
; return address as CS:offset at [BP-6..9]
; old BP at [BP-10..11]
spbias equ 10 ; new sp points at last local stack variable
assume cs:$paint,ds:$paint
paint PROC FAR
push bp ; Save caller's frame pointer
mov bp,sp ; and set up our own.
add bp,4+2*argcnt ; Point to top of arg list on stack
sub sp,spbias ; Reserve space for locals on stack
push ds
mov ax,cs
mov ds,ax ; establish ds=cs addressability
mov si,x
mov di,y
mov ax,colval
mov color,ax
mov dx,color ; initialize paint color
;
; bp no longer used as variable pointer
;
mov ax,quadscreen
mov es,ax ; establish quadscreen addressability
;
; initialize paint stack
;
mov bp,paintstack ; bp is set to top of stack
call pushpaint ; push interior point onto stack
;
; main loop for painting
;
paint1:
mov ax,paintstack ; stack empty?
cmp bp,ax
jne paint2 ; continue if not
jmp endpaint ; else exit
;
; get the next place to paint
;
paint2:
call poppaint ; pop the next place to paint
call locate ; color is returned in al
cmp al,dl ; is it filled?
je paint1 ; yes
cmp al,dh ; is it boundary?
je paint1 ; yes
cmp di,scr_top ; top of screen?
jl paint1 ; no
cmp di,scr_bot ; bottom of screen?
jg paint1 ; yes
;
; move right until boundary reached
;
paint3:
inc si ; x <-- x + 1
call locate ; look right
dec si ; restore x
cmp al,dl ; is it filled?
je paint4 ; yes
cmp al,dh ; is it boundary?
je paint4 ; yes
cmp si,scr_right ; at right screen boundary?
je paint4 ; yes
inc si ; x <-- x + 1
jmp paint3
;
; push above and below
;
paint4:
dec di ; y <-- y - 1
call locate ; check above
mov bh,al ; save above state
cmp al,dl ; is it filled?
je paint5 ; yes
cmp al,dh ; is it boundary color?
je paint5 ; yes
call pushpaint ; no, push above
paint5:
inc di ; restore y
inc di ; y <-- y + 1
call locate ; check below
mov bl,al ; save below state
cmp al,dl ; is it filled?
je paint6 ; yes
cmp al,dh ; is it boundary color
je paint6
call pushpaint ; push below
paint6:
dec di ; restore y
;
; anchor the end point of the scan line
;
mov x2,si ; store x-coordinate of end of scan line
mov y2,di ; store y-coordinate of end of scan line
;
; plot as we scan left, checking above and below
;
paint7:
dec di ; y <-- y - 1
call locate ; check above
cmp al,dl ; is it filled?
je paint9
cmp al,dh ; is it boundary color?
je paint9
cmp bh,dl ; last above filled?
je paint8 ; yes
cmp bh,dh ; boundary color?
jne paint9 ; no
paint8:
call pushpaint ; push above if new place to paint
paint9:
mov bh,al ; update last above
inc di ; restore y
inc di ; y <-- y + 1
call locate ; check below
cmp al,dl ; is it filled?
je paint11 ; yes
cmp al,dh ; is it boundary color?
je paint11 ; yes
cmp bl,dl ; last below filled?
je paint10 ; yes
cmp bl,dh ; was it boundary color?
jne paint11 ; no
paint10:
call pushpaint ; push below if new place to paint
paint11:
dec di ; restore y
mov bl,al ; update last below
dec si ; move left, x <-- x - 1
jl paint12 ; stop the scan if too far left
call locate ; check the point
cmp al,dl ; hit filled yet?
je paint12 ; yes, next scan line
cmp al,dh ; hit boundary yet?
jne paint7 ; no
paint12: ; yes, continue next scan line
inc si ; restore x
mov x1,si ; store x-coordinate of start
mov y1,di ; store y-coordinate of start
call setbox ; plot the scan line
jmp paint1 ; next place to paint
endpaint:
pop ds ; restore ds
add sp,spbias ; restore original sp at entry
pop bp ; and callers frame pointer
ret 2*argcnt ; and return discarding stack arguments
paint ENDP
;-----------------------------------------------------------------------
$paint ENDS
END